/*
 * UdpRtpSocket.cpp
 *
 *  Created on: 2018年5月1日
 *      Author: zhengboyuan
 */

#include "UdpRtpSocket.h"
#include "RtpPort.h"
#include "RtpHeader.h"
#include "RtpConst.h"

namespace av
{

UdpRtpSocket::UdpRtpSocket():
		m_socket()
{
	m_sendBuffer.ensure(RtpConst::MTU_MAX_SIZE);
	m_recvBuffer.ensure(RtpConst::MTU_MAX_SIZE * 2);
}

UdpRtpSocket::~UdpRtpSocket()
{
	close();
}

bool UdpRtpSocket::open(const char* ip, int port)
{
	if (!m_socket.open(SOCK_DGRAM))
	{
		return false;
	}

	m_socket.setReuse();

	m_socket.trySetSendBufferSize(SEND_BUFFER_SIZE, MIN_BUFFER_SIZE);
	m_socket.trySetRecvBufferSize(SEND_BUFFER_SIZE, MIN_BUFFER_SIZE);


	bool done = false;
	if (port == 0)
	{
		for (size_t i = 0; i < (RtpPort::count() + 1); ++ i)
		{
			port = RtpPort::make();

			comn::SockAddr addr(ip, port);
			if (0 == m_socket.bind(addr))
			{
				m_addr = addr;
				done = true;
				break;
			}
		}
	}
	else
	{
		comn::SockAddr addr(ip, port);
		if (0 == m_socket.bind(addr))
		{
			m_addr = addr;
			done = true;
		}
	}

	if (!done)
	{
		close();
		return false;
	}

	m_socketPair.open();

	return true;
}

void UdpRtpSocket::close()
{
	m_socket.close();
	m_socketPair.close();
}

bool UdpRtpSocket::isOpen()
{
	return m_socket.isOpen();
}

NetAddress UdpRtpSocket::getLocalAddr()
{
	return NetAddress(m_addr.getIPAddr(), m_addr.getPort());
}

bool UdpRtpSocket::send(RtpPacket& packet, int ms)
{
	if (!m_socket.isOpen())
	{
		return false;
	}

	if (m_peerAddr.getPort() == 0)
	{
		return true;
	}

	m_sendBuffer.ensure(packet.size + sizeof(RTP_FIXED_HEADER));

	RTP_FIXED_HEADER* header = (RTP_FIXED_HEADER*)m_sendBuffer.data();

	header->version = 2;
	header->marker = packet.mark;
	header->timestamp = htonl(m_initTime + packet.ts);
	header->ssrc = htonl(m_ssrc);
	header->seq_no = htons(m_seq ++);
	header->payload = packet.pt;

	uint8_t* data = m_sendBuffer.data() + sizeof(RTP_FIXED_HEADER);
	memcpy(data, packet.data, packet.size);

	data = m_sendBuffer.data();
	int size = packet.size + sizeof(RTP_FIXED_HEADER);

	m_socket.sendTo((char*)data, size, 0, m_peerAddr);

	return true;
}

bool UdpRtpSocket::recv(RtpPacketHandler handler, int ms)
{
    if (!m_socketPair.checkReadable(m_socket.getHandle(), ms))
	//if (!m_socket.checkReadable(ms))
	{
		return false;
	}

	comn::SockAddr addr;
	int size = m_socket.receiveFrom((char*)m_recvBuffer.data(), m_recvBuffer.capacity(), 0, addr);
	if (size <= 0)
	{
		return false;
	}

	uint8_t* data = m_recvBuffer.data();
	RTP_FIXED_HEADER* header = (RTP_FIXED_HEADER*)data;

	data += sizeof(RTP_FIXED_HEADER);
	size -= sizeof(RTP_FIXED_HEADER);

	RtpPacket pkt;
	memset(&pkt, 0, sizeof(pkt));
	pkt.pt = header->payload;
	pkt.mark = header->marker;
	pkt.ts = ntohl(header->timestamp);
	pkt.data = data;
	pkt.size = size;

	handler(pkt);

	return true;
}

bool UdpRtpSocket::isConnected()
{
	return isOpen();
}

void UdpRtpSocket::abort()
{
    m_socketPair.makeReadable();
}



} /* namespace av */
